from google.appengine.api.urlfetch import fetch
from xml.dom.minidom import parseString
from datetime import datetime, timedelta
import re
import time
from string import Template
import logging

forecast_url = "http://www.weather.gov/forecasts/xml/SOAP_server/ndfdXMLclient.php?whichClient=NDFDgen&zipCodeList=%s&product=time-series&maxt=maxt&mint=mint&temp=temp&wspd=wspd&wdir=wdir&wx=wx&rh=rh&snow=snow&wwa=wwa&sky=sky&appt=appt&Submit=Submit"
geocode_url = 'http://geocoder.us/service/csv/geocode?zip=%s'

cache = {}

class TimeLayout:
	key = ''
	times = []
	interval = 1

	
class Seria:
	name = ''
	timelayout = 0
	values = []

def td(s, style = '', fgcolor = ''):
	if len(fgcolor) > 0:
		s = '<font color="' + fgcolor + '">' + s + '</color>'

	if len(style) > 0:
		return '<td class="' + style + '">' + s + '</td>\n'
	return '<td>' + s + '</td>\n'

def tr(s, style = ''):
	if len(style) > 0:
		return '<tr class="' + style + '">\n' + s + '</tr>\n'
	return '<tr>\n' + s + '</tr>\n'


winddir = ['N',       'NE',      'E',    'SE', 'S', 'SW', 'W', 'NW']
windarr = ['&#8593;', '&#8599;', '&#8594;', '&#8600;', '&#8595;', '&#8601;', '&#8592;', '&#8598;']
HOURS = 24*7
short = False

def get(el, name):
	els = el.getElementsByTagName(name)
	if len(els) == 0:
		return ''
	return els[0].firstChild.nodeValue
	
def dump(o):
	for property, value in inspect.getmembers(o):
#			print(property, ": ", len(value))
		print(property)


def getTimeLayouts(dom):
	timelayouts = {}
	starttime = datetime.max
	data = dom.firstChild.getElementsByTagName("data")[0]
	tlayouts = data.getElementsByTagName("time-layout")
	for tlayout in tlayouts:
		timelayout = TimeLayout()
		timelayout.key = tlayout.getElementsByTagName("layout-key")[0].firstChild.nodeValue
		timelayout.times = []
		m = re.search('k-p([0-9]+)h-', timelayout.key)
		timelayout.interval = int(m.group(1))

		times = tlayout.getElementsByTagName("start-valid-time")
		for t in times:
			timestr = t.firstChild.nodeValue[:19]
			tm = datetime.strptime(timestr, '%Y-%m-%dT%H:00:00')
			timelayout.times.append(tm)
			if tm < starttime:
				starttime = tm
		timelayouts[timelayout.key] = timelayout
	return timelayouts, starttime


def getIntencity(intencity):
	if intencity == 'light':
		return 'LT'
	if intencity == 'very light':
		return 'lt'
	if intencity == 'moderate':
		return 'MO'
	if intencity == 'heavy':
		return 'HV'
	return '??'

def parseWeatherConditions(node, timelayout):
	series = []
	seriaWeatherType = Seria()
	seriaWeatherType.name = 'Weather Type'
	seriaWeatherType.timelayout = timelayout
	seriaWeatherType.values = []
	
	seriaVisibility = Seria()
	seriaVisibility.name = 'Visibility'
	seriaVisibility.timelayout = timelayout
	seriaVisibility.values = []
	
	seriaSnowIntensity = Seria()
	seriaSnowIntensity.name = 'Snow Intensity'
	seriaSnowIntensity.timelayout = timelayout
	seriaSnowIntensity.values = []
	
	seriaRainIntensity = Seria()
	seriaRainIntensity.name = 'Rain Intensity'
	seriaRainIntensity.timelayout = timelayout
	seriaRainIntensity.values = []
	
	conditions = node.getElementsByTagName('weather-conditions')
	for cond in conditions:
		values = cond.getElementsByTagName('value')
		visibility = ''
		snowint = ''
		rainint = ''
		for v in values:
			wt = v.getAttribute('weather-type')
			intencity = v.getAttribute('intensity')
			
			if wt == 'snow' or wt == 'snow showers':
				snowint = getIntencity(intencity)
				
			if wt == 'rain' or wt == 'rain showers':
				rainint = getIntencity(intencity)
				
			vnodes = v.getElementsByTagName('visibility')
			if len(vnodes) > 0:
				if vnodes[0].hasChildNodes():
					visibility = vnodes[0].firstChild.nodeValue
					
		seriaSnowIntensity.values.append(snowint)
		seriaRainIntensity.values.append(rainint)
		seriaWeatherType.values.append(' ')
		seriaVisibility.values.append(visibility)
	
	series.append(seriaSnowIntensity)
	series.append(seriaRainIntensity)
#			series.append(seriaWeatherType)
	series.append(seriaVisibility)
	return series

def parseValues(node, timelayout):
	seria = Seria()
	name = node.getElementsByTagName("name")[0]
	seria.name = name.firstChild.nodeValue
	seria.timelayout = timelayout
	seria.values = []
	
	values = node.getElementsByTagName('value')
	for v in values:
		nv = ''
		if v.hasChildNodes():
			nv = v.firstChild.nodeValue

		if seria.name.find('Temperature') >= 0:
			nv = "%3d" % ((int(nv) - 32) * 5 / 9)
		if seria.name == 'Relative Humidity':
			nv = int(nv)
			if nv < 0:
				nv = ''
		if seria.name == 'Wind Direction':
			nv = windarr[int(((360-int(nv)+22.5) % 360) / 45)]
		if seria.name == 'Snow Amount':
			if nv == '0':
				nv = ''
		seria.values.append(nv)
	return seria

def parseData(dom):
	timelayouts, starttime = getTimeLayouts(dom)
		
	series = []
	parameters = dom.firstChild.getElementsByTagName("parameters")[0]
	for node in parameters.childNodes:
		if node.nodeType != dom.ELEMENT_NODE:
			continue
			
		name = node.getElementsByTagName("name")[0]
		timelayout = timelayouts[node.getAttribute('time-layout')]

		if name.firstChild.nodeValue == 'Weather Type, Coverage, and Intensity':
			series += parseWeatherConditions(node, timelayout)
		else:
			seria = parseValues(node, timelayout)
			if seria.name.find('Daily') == 0:
				continue
			series.append(seria)
	return series, starttime
	
def parseHead(dom):
	head = dom.getElementsByTagName('head')[0]
	product = head.getElementsByTagName('product')[0]
	creationdate = product.getElementsByTagName('creation-date')[0]
	refreshfrequency = creationdate.getAttribute('refresh-frequency')
	return creationdate.firstChild.nodeValue, refreshfrequency

def loadCurrentConditions(station_id):
	url = 'http://www.weather.gov/xml/current_obs/%s.xml' % station_id
	resp = fetch(url)
	dom = parseString(resp.content)
	cu = dom.getElementsByTagName("current_observation")[0]
	timestr = get(cu, "observation_time_rfc822")
	tm = time.strptime(timestr[:-6], "%a, %d %b %Y %H:%M:%S")
	station = get(cu, "station_id")
	temp = get(cu, "temp_c")
	humy = get(cu, "relative_humidity")
	pressure = float(get(cu, "pressure_mb")) * 0.75006
	weather = get(cu, "weather")
	wind_mph = float(get(cu, "wind_mph"))
	wind_dir = get(cu, "wind_dir")
	water_temp = get(cu, "water_temp_c")
	wave_height = get(cu, "wave_height_m")
	
	if wind_dir == "North": wind_dir = "N "
	if wind_dir == "South": wind_dir = "S "
	if wind_dir == "West": wind_dir = "W"
	if wind_dir == "East": wind_dir = "E "
	if wind_dir == "Northeast": wind_dir = "NE"
	if wind_dir == "Southeast": wind_dir = "SE"
	if wind_dir == "Southwest": wind_dir = "SW"
	if wind_dir == "Northwest": wind_dir = "NW"
	if wind_dir == "Variable": wind_dir = "Var"

	if wave_height != '':
		t = "%s %5s: %4sC      %.0fmm %2.0fmph %3s, WATER %sC WAVE %sm" % (time.strftime("%H:%M", tm), station, temp, pressure, wind_mph, wind_dir, water_temp, wave_height)
	else:
		t = "%s %5s: %4sC %3s%% %.0fmm %2.0fmph %3s, %s" % (time.strftime("%H:%M", tm), station, temp, humy, pressure, wind_mph, wind_dir, weather)
	
	return t

def getCurrentConditions(station_id):
	fetchtime = 0
	if 'wxcurrent_fetchtime_' + station_id in cache:
		fetchtime = cache['wxcurrent_fetchtime_' + station_id]

	if fetchtime is None or time.time() - fetchtime > 300:
		s = loadCurrentConditions(station_id)
		cache['wxcurrent_' + station_id] = s
		cache['wxcurrent_fetchtime_' + station_id] = time.time()

	s = cache['wxcurrent_' + station_id]
	return s


	
def createTable(series, timestart):
	table = [['' for row in range(0, HOURS + 1)] for i in range(0, len(series) + 2)]

	table[0][0] = 'Filled'
	table[1][0] = 'Day'
	table[2][0] = 'Hour'
	
	for i in range(2, HOURS + 1):
		tm = timestart + timedelta(hours = i - 2)
		hour = ' ' + tm.strftime('%H')
		day = ' ' + tm.strftime('%a')[:2]
		
		table[0][i] = 0
		table[1][i] = day
		table[2][i] = hour
		
	rx = 3
	for s in series:
		if s.name == 'Watches, Warnings, and Advisories':
			continue
			
		if short == True:
			if s.name == 'Temperature': s.name = 'temp'
			elif s.name == 'Apparent Temperature': s.name = 'atemp'
			elif s.name == 'Wind Speed': s.name = 'wspd'
			elif s.name == 'Wind Direction': s.name = 'wdir'
			elif s.name == 'Relative Humidity': s.name = 'humy'
			elif s.name == 'Cloud Cover Amount': s.name = 'cloud'
			elif s.name == 'Snow Amount': s.name = 'snow'
			elif s.name == 'Rain Intensity': s.name = 'rain'
			elif s.name == 'Visibility': s.name = 'visy'
		else:
			s.name = '<nobr>' + s.name + '</nobr>'
		
		table[rx][0] = s.name
		table[rx][1] = 0 # empty flag
		for cx in range(0, len(s.timelayout.times)):
			delta = s.timelayout.times[cx] - timestart
			d = delta.days * 24 + int(delta.seconds / 3600) + 1
			if d > 0 and d < HOURS:
				if type(s.values[cx]) == int or len(s.values[cx]) > 0:
					table[rx][d] = s.values[cx]
					table[0][d] = 1
					table[rx][1] = 1
		rx += 1

	return table
	
def rowToHtml(table, rowNum, refRowNum, tdstyle = 'td'):
	
	if table[rowNum][1] == 0:
		return ''
	
	s = ''
	prevday = ''
	idx = 1
	
	style = ''
	if refRowNum != 0:
		style = tdstyle + '0'
	
	row = table[rowNum]
	refrow = table[refRowNum]
	t = td(row[0], 'left')
	for cx in range(2, HOURS + 1):
		if table[0][cx] == 1:
			if refRowNum != 0 and prevday != str(refrow[cx]):
				idx += 1
				style = tdstyle + str(idx % 2)
				if rowNum == 2:
					style = tdstyle + 'u' + str(idx % 2)
					
			fgcolor = ''
			if row[0] == 'Temperature':
				if len(row[cx]) > 0 and  int(row[cx]) < 0:
					fgcolor = 'blue'
					
			t += td(str(row[cx]), style, fgcolor)
			prevday = str(refrow[cx])
			
	return tr(t)

def tableToHtml(table):
	s = ''
	for cx in range(2, HOURS + 1):
		if table[0][cx] == 1:
			i = 3
			curr_temp = table[3][i]
			while curr_temp == '':
				curr_temp = table[3][i]
				i += 1

			s += '<b>' + str(curr_temp) + 'C</b>'
			for rx in range(3, len(table)):
				if table[rx][0].find('Rain') >= 0 and table[rx][cx] != '' >= 0:
					s += ' <b>RAIN: ' + str(table[rx][cx]) + '</b>'
					break
			break
			
	s += '<table>\n'
	s += rowToHtml(table, 1, 1, 'th')
	s += rowToHtml(table, 2, 1, 'th')
		
	for rx in range(3, len(table)):
		s += rowToHtml(table, rx, 1, 'td')
	
	s += '</table>\n'

	return s

def isError(dom):
	errors = dom.getElementsByTagName("error")
	if len(errors) > 0:
		print(errors[0].nodeName)
		return True
	return False

def getAddress(zip):
	url = geocode_url % zip
	resp = fetch(url)
	return resp.content

def getForecastHtml(zip):
	url = forecast_url % zip
	fetched_time = 0
	
	if url + '_fetched' in cache:
		fetched_time = cache[url + '_fetched']

	if fetched_time is None or time.time() - fetched_time > 900:
		resp = fetch(url)
		text = resp.content
		cache[url] = text
		cache[url + '_fetched'] = time.time()
	text = cache[url]
	dom = parseString(text)
	
	if isError(dom):
		return ('<div>Forecast for zip code ' + zip + ' is not available</div>', False)
	
	creationdate, refreshfrequency = parseHead(dom)
	series, starttime = parseData(dom)
	table = createTable(series, starttime)

	html = ''
	html += tableToHtml(table) + '<br>'
	return (html, True)

page = '''
<html>
<head>
<title>$title</title>
<link rel="stylesheet" type="text/css" href="/files/main.css" />
</head>
<body>
$body
<a href="/zip/11230">brk</a>
<a href="/zip/10001">mnh</a>
<a href="/zip/02062">bos</a>
<a href="/zip/30303">atl</a>
<a href="/zip/55901">rch</a>
<a href="/zip/00822">vi</a>
</body>
</html>
'''
	
def processZips(req):
	
	arr = req.split('/')
	
	if len(arr) < 3:
		zips = '11230'
	else:
		zips = arr[2];
		
	body = ''
	for zip in zips.split(','):
		(text, hr) = getForecastHtml(zip)
		if hr == False:
			return None
		body += text;

	t = Template(page)
	d = dict(body = body, title = zips)

	return t.safe_substitute(d)


def parse(p, shortReport):
	global HOURS
	global short
#	url = forecast_url % 11230
#	resp = fetch(url)
	if shortReport == True:
		HOURS = 48
		short = shortReport
	s = processZips(p)
	return s


